/************************************
*  Copyright (C) 2010 Julien MOTTIN.
*  Contact: julien.mottin@gueydan.eu
*
*  This file is part of Gueydan.
*
*  Gueydan is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  Gueydan is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with Gueydan.  If not, see <http://www.gnu.org/licenses/>.
*
*************************************/
// Local
#include "beerxmlhandler.h"
#include "beerxmlobject.h"
#include "hop.h"
#include "fermentable.h"
#include "misc.h"
#include "water.h"
#include "yeast.h"
#include "mash.h"
#include "mash_step.h"
#include "style.h"
#include "recipe.h"
#include "xmldico.h"

// QtCore
#include <QFileInfo>
#include <QFile>
#include <QDomDocument>
#include <QDomElement>
#include <QString>
#include <QTextStream>

// Standard library
#include <stddef.h> // NULL macro

BeerXMLHandler::BeerXMLHandler()
:_pObject(NULL)
{
}

BeerXMLHandler::BeerXMLHandler(BeerXMLObject *ipObject)
:_pObject(NULL)
{
    if (ipObject)
        ipObject->AddRef();

    _pObject = ipObject;
}

BeerXMLHandler::BeerXMLHandler(const BeerXMLObject *ipObject)
:_pObject(NULL)
{
    if (ipObject)
        ipObject->AddRef();

    _pObject = (BeerXMLObject*)ipObject;
}

BeerXMLHandler::~BeerXMLHandler()
{
    if (_pObject)
        _pObject->Release();
    _pObject = NULL;
}

BeerXMLHandler::BeerXMLHandler(const BeerXMLHandler &iOther)
:_pObject(NULL)
{
    _pObject = iOther._pObject;
    if (_pObject)
        _pObject->AddRef();
}

BeerXMLHandler & BeerXMLHandler::operator=(const BeerXMLHandler &iOther)
{
    if (this!=&iOther)
    {
        if (_pObject)
            _pObject->Release();

        _pObject = iOther._pObject;
        if (_pObject)
            _pObject->AddRef();
    }
    return *this;
}

BeerXMLHandler & BeerXMLHandler::operator=(BeerXMLObject* ipObject)
{
    if (_pObject)
        _pObject->Release();
    
    _pObject = ipObject;
    if (_pObject)
        _pObject->AddRef();
    
    return *this;
}

BeerXMLHandler & BeerXMLHandler::operator=(const BeerXMLObject* ipObject)
{
    if (_pObject)
        _pObject->Release();
    
    _pObject = (BeerXMLObject*)ipObject;
    if (_pObject)
        _pObject->AddRef();
    
    return *this;    
}

BeerXMLObject * BeerXMLHandler::operator->() const
{
    return _pObject;
}

BeerXMLHandler::operator const BeerXMLObject *() const
{
    return _pObject;
}

bool BeerXMLHandler::operator!() const
{
    return (!_pObject);
}

bool BeerXMLHandler::operator==(const BeerXMLHandler &iOther) const
{
    // Pointer equality
    if (_pObject==iOther._pObject) return true;
    
    // Deep instance equality
    if (_pObject && iOther._pObject)
    {
        return (*_pObject==*iOther._pObject);
    }
    return false;
}

BeerXMLHandler::operator Hop*() const
{
    return qobject_cast<Hop*>(_pObject);
}

BeerXMLHandler::operator Fermentable*() const
{
    return qobject_cast<Fermentable*>(_pObject);
}

BeerXMLHandler::operator Misc*() const
{
    return qobject_cast<Misc*>(_pObject);
}

BeerXMLHandler::operator Water*() const
{
    return qobject_cast<Water*>(_pObject);
}

BeerXMLHandler::operator Yeast*() const
{
    return qobject_cast<Yeast*>(_pObject);
}

BeerXMLHandler::operator Mash*() const
{
    return qobject_cast<Mash*>(_pObject);
}

BeerXMLHandler::operator MashStep*() const
{
    return qobject_cast<MashStep*>(_pObject);
}

BeerXMLHandler::operator Style*() const
{
    return qobject_cast<Style*>(_pObject);
}

BeerXMLHandler::operator Recipe*() const
{
    return qobject_cast<Recipe*>(_pObject);
}

int BeerXMLHandler::CreateFromFile(const QString &iBeerXMLFile)
{
    int oRC = 1;
    QFileInfo Info(iBeerXMLFile);

    // Check consistency
    if (Info.exists() && Info.isFile() && Info.isReadable())
    {
        // Check format
        oRC = 2;
        if (Info.suffix().toLower()=="xml")
        {
            QFile File(iBeerXMLFile);
            if (File.open(QIODevice::ReadOnly))
            {
                // Parse
                QDomDocument Doc;
                Doc.setContent(&File);

                // Call Create from xml node
                oRC = CreateFromXMLNode(Doc);

                // Close
                File.close();
            }
        }
    }

    return oRC;
}

int BeerXMLHandler::SaveToFile(const QString &iBeerXMLFile) const
{
    int oRC = 1;
    if (_pObject)
    {
        QDomDocument doc("BEERXML");
        QFile File(iBeerXMLFile);
        File.open(QIODevice::ReadWrite);
        QTextStream stream(&File);
        if (0==_pObject->ToXMLNode(doc))
        {
            doc.save(stream,2);
            oRC = 0;
        }
        File.close();
    }
    return oRC;
}

int BeerXMLHandler::CreateFromXMLNode(const QDomNode &iNode)
{
    // init
    int oRC = 1;       
    QDomElement root;

    // case document
    if (iNode.isDocument())
    {
        QDomDocument doc = iNode.toDocument();
        root = doc.documentElement();
    }
    else
        root = iNode.toElement();

    // Check consistency
    if (!root.isNull())
    {
        // init
        QString rootName = root.nodeName();
        BeerXMLObject *pObject = NULL;

        // parse
        if (rootName==HOP)
            pObject = new Hop();
        else if (rootName==FERMENTABLE)
            pObject = new Fermentable();
        else if (rootName==YEAST)
            pObject = new Yeast();
        else if (rootName==MISC)
            pObject = new Misc();
        else if (rootName==WATER)
            pObject = new Water();
        else if (rootName==MASHSTEP)
            pObject = new MashStep();
        else if (rootName==MASH)
            pObject = new Mash();
        else if (rootName==STYLE)
            pObject = new Style();
        else if (rootName==RECIPE)
            pObject = new Recipe();

        if (pObject)
        {
            // Parse & affect (to perform clean & addref)
            Q_ASSERT(0==pObject->CreateFromXMLNode(root));
            {
                (*this) = pObject;
                oRC = 0;
            }

            // Clean
            pObject->Release();
        }
    }
    return oRC;
}
